#include "MultiItemsSelectWindow.h"

#define QTC_ASSERT(cond, action) \
  if (cond) {                    \
  } else {                       \
    action;                      \
  }                              \
  do {                           \
  } while (0)

#include <QApplication>
#include <QFocusEvent>
#include <QHeaderView>
#include <QScrollBar>
#include <QVBoxLayout>

enum class Role { ItemData = Qt::UserRole };

MultiItemsSelectWindow::MultiItemsSelectWindow(MultiItemsDataProvider* hp,
                                               QWidget* parent, bool showIcon)
    : QFrame(parent, Qt::Popup),
      mDataProvider(hp),
      mShowIcon(showIcon),
      mEditorList(new OpenViewsTreeWidget(this)) {
  setMinimumSize(300, 200);
  mEditorList->setColumnCount(1);
  mEditorList->header()->hide();
  mEditorList->setIndentation(0);
  mEditorList->setSelectionMode(QAbstractItemView::SingleSelection);
  mEditorList->setTextElideMode(Qt::ElideMiddle);
  mEditorList->installEventFilter(this);

  setFrameStyle(mEditorList->frameStyle());
  mEditorList->setFrameStyle(QFrame::NoFrame);

  QVBoxLayout* layout = new QVBoxLayout(this);
  layout->setMargin(0);
  layout->addWidget(mEditorList);

  connect(mEditorList, &QTreeWidget::itemClicked, this,
          &MultiItemsSelectWindow::editorClicked);

  setVisible(false);
}

void MultiItemsSelectWindow::gotoNextItem() {
  if (isVisible()) {
    selectPreviousEditor();
  } else {
    addItems();
    selectPreviousEditor();
    showPopupOrSelectDocument();
  }
}

void MultiItemsSelectWindow::gotoPreviousItem() {
  if (isVisible()) {
    selectNextEditor();
  } else {
    addItems();
    selectNextEditor();
    showPopupOrSelectDocument();
  }
}

void MultiItemsSelectWindow::showPopupOrSelectDocument() {
  if (QApplication::keyboardModifiers() == Qt::NoModifier) {
    selectAndHide();
  } else {
    QWidget* activeWindow = QApplication::activeWindow();
    if (!activeWindow) return;

    QWidget* referenceWidget = activeWindow;
    const QPoint p = referenceWidget->mapToGlobal(QPoint(0, 0));

    this->setMaximumSize(
        qMax(this->minimumWidth(), referenceWidget->width() / 2),
        qMax(this->minimumHeight(), referenceWidget->height() / 2));
    this->adjustSize();
    this->move((referenceWidget->width() - this->width()) / 2 + p.x(),
               (referenceWidget->height() - this->height()) / 2 + p.y());
    this->setVisible(true);
  }
}

void MultiItemsSelectWindow::selectAndHide() {
  setVisible(false);
  selectEditor(mEditorList->currentItem());
}

void MultiItemsSelectWindow::setVisible(bool visible) {
  QWidget::setVisible(visible);
  if (visible) setFocus();
}

bool MultiItemsSelectWindow::eventFilter(QObject* obj, QEvent* e) {
  if (obj == mEditorList) {
    if (e->type() == QEvent::KeyPress) {
      QKeyEvent* ke = static_cast<QKeyEvent*>(e);
      if (ke->key() == Qt::Key_Escape) {
        setVisible(false);
        return true;
      }
      if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter) {
        selectEditor(mEditorList->currentItem());
        return true;
      }
    } else if (e->type() == QEvent::KeyRelease) {
      QKeyEvent* ke = static_cast<QKeyEvent*>(e);
      if (ke->modifiers() == 0
          /*HACK this is to overcome some event inconsistencies between
             platforms*/
          || (ke->modifiers() == Qt::AltModifier &&
              (ke->key() == Qt::Key_Alt || ke->key() == -1))) {
        selectAndHide();
      }
    }
  }
  return QWidget::eventFilter(obj, e);
}

void MultiItemsSelectWindow::focusInEvent(QFocusEvent*) {
  mEditorList->setFocus();
}

void MultiItemsSelectWindow::selectUpDown(bool up) {
  int itemCount = mEditorList->topLevelItemCount();
  if (itemCount < 2) return;
  int index = mEditorList->indexOfTopLevelItem(mEditorList->currentItem());
  if (index < 0) return;
  QTreeWidgetItem* editor = 0;
  int count = 0;
  while (!editor && count < itemCount) {
    if (up) {
      index--;
      if (index < 0) index = itemCount - 1;
    } else {
      index++;
      if (index >= itemCount) index = 0;
    }
    editor = mEditorList->topLevelItem(index);
    count++;
  }
  if (editor) {
    mEditorList->setCurrentItem(editor);
    ensureCurrentVisible();
  }
}

void MultiItemsSelectWindow::selectPreviousEditor() { selectUpDown(false); }

QSize MultiItemsSelectWindow::OpenViewsTreeWidget::sizeHint() const {
  return QSize(
      sizeHintForColumn(0) + verticalScrollBar()->width() + frameWidth() * 2,
      viewportSizeHint().height() + frameWidth() * 2);
}

QSize MultiItemsSelectWindow::sizeHint() const {
  return mEditorList->sizeHint() + QSize(frameWidth() * 2, frameWidth() * 2);
}

void MultiItemsSelectWindow::selectNextEditor() { selectUpDown(true); }

void MultiItemsSelectWindow::addItems() {
  mEditorList->clear();
  auto& history = mDataProvider->MIDP_getItems();
  for (auto& i : history) {
    addItem(i);
  }
}

void MultiItemsSelectWindow::selectEditor(QTreeWidgetItem* item) {
  if (!item) return;
  auto index = item->data(0, int(Role::ItemData)).value<MIDPKey>();
  mDataProvider->MIDP_selected(index);
}

void MultiItemsSelectWindow::editorClicked(QTreeWidgetItem* item) {
  selectEditor(item);
  setFocus();
}

void MultiItemsSelectWindow::ensureCurrentVisible() {
  mEditorList->scrollTo(mEditorList->currentIndex(),
                        QAbstractItemView::PositionAtCenter);
}

void MultiItemsSelectWindow::addItem(MIDPKey index) {
  QTreeWidgetItem* item = new QTreeWidgetItem();

  if (mShowIcon) item->setIcon(0, mDataProvider->MIDP_getIcon(index));
  item->setText(0, mDataProvider->MIDP_getItemName(index));
  item->setData(0, int(Role::ItemData), QVariant::fromValue(index));
  item->setTextAlignment(0, Qt::AlignLeft);

  mEditorList->addTopLevelItem(item);

  if (mEditorList->topLevelItemCount() == 1) mEditorList->setCurrentItem(item);
}
